/**
 ******************************************************************************
 * @file	interface/fpga-2016.c
 * @brief	interface/fpga-2016.c
 fpga-2016 存储器通信

采用原始 fpga-2016 SPI通信协议

bug
    fpga 自身返回序列有缺陷，普通的控制命令容易错乱，主控端需要多次重复读取，
    返回OTDR数据是否有错误无法得知，
 *
 @section Platform
    -# linux-2.6
 @section Library
    -# null
 *
 * @section Depend
 *	-# spi_mxs.ko
 *	-# spidev.ko
 *	-# libpthread.so

- 2016-10-21,MenglongWu,MenglongWoo@aliyun.com
*/

#include <autoconfig.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#include <sys/ioctl.h>

#include <assert.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <stdio.h>

#include "proginfo.h"
#include "fpga-2016.h"
#include "printl.h"

#include "fpga_encode.h"
// #include "fpga-otdr-cfg.h"

#define SPI_TP_BEGIN  _IOR(SPI_IOC_MAGIC, 5, __u8)
#define SPI_TP_COMMIT _IOR(SPI_IOC_MAGIC, 6, __u8)

#ifdef NDEBUG
#define this_dbg(fmt, ...)
#else
#define this_dbg(fmt, ...) PRINTL(OTDR_DBG_LEVEL, C_DEBUG fmt C_NORMAL, ##__VA_ARGS__)
#endif

#ifdef CONFIG_USE_PTHREAD_FPGA_OTDR
#define mutex_init(x, y) pthread_mutex_init((x), (y))
#define mutex_destroy(x) pthread_mutex_destroy((x))
#define mutex_lock(x)                \
    ioctl(dev->fd, SPI_TP_BEGIN, 0); \
    pthread_mutex_lock((x));
#define mutex_unlock(x)        \
    pthread_mutex_unlock((x)); \
    ioctl(dev->fd, SPI_TP_COMMIT, 0);
#else
#define mutex_init(x, y)
#define mutex_destroy(x)
#define mutex_lock(x)   ioctl(dev->fd, SPI_TP_BEGIN, 0);
#define mutex_unlock(x) ioctl(dev->fd, SPI_TP_COMMIT, 0);
#endif

#define ACK_CMD_OFFSET  (9)
#define ACK_CODE_OFFSET (10)
#define ACK_CMD(x)      (*((x) + ACK_CMD_OFFSET))
#define ACK_CODE(x)     (*((x) + ACK_CODE_OFFSET))

#define LEN_OTDR_CACHE  (32000 * 4 + 4000)
#define CHKSUM_OFFSET   (14)

static unsigned int _fpga_transfer(struct fpga_dev *dev, unsigned char cmd, char *tbuf, int tlen, char *rbuf, int rlen);
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
static unsigned int _fpga_alive(struct fpga_dev *dev);
static const char  *dev_desc   = "fpga-2016";
char               *otdr_cache = NULL;
/*
《fpga-2016 TR600plus—CPU与FPGA通信命令协议》 2016-11-4 协议格式
最小帧长度 10 Byte
-------------------------------------------------------------------------------
glink (5)  |  len (1) |  unuse1 (1)  |  cmd (1)  |  data (n)  |  checksum (2)
-------------------------------------------------------------------------------

《TR600plus—CPU与FPGA通信命令协议.doc》 2016-11-24 协议格式
最小帧长度 12 Byte
-------------------------------------------------------------------------------
glink (5)  |  len (2)  |  unuse1 (1)  | addr (1) |  cmd (1)  |  data (n)  |  checksum (2)
-------------------------------------------------------------------------------

FPGA返回格式
其中 cmd 固定为 0xf0
data 通常为 1-8 byte
-------------------------------------------------------------------------------
glink (5)  |  len (2)  |  unuse1 (1)  | addr (1) |  cmd (1)  |  data(n)
-------------------------------------------------------------------------------

*/
#define MIN_TX_PACK_SIZE (12)  // 最小发送帧长度
static struct fpga_2016
{
    char head[5];
    /*
    FPGA 里忽略 glink 与 cmd 之间的 4 byte
    */

#if 1
    char len;
    char unuse0;
#else
    short len;
#endif
    char unuse1;
    char addr;
    char cmd;
} __attribute__((__packed__));


// #define MIN_RX_PACK_SIZE (12)		// 最小接收帧长度
#define MIN_RX_PACK_SIZE (38)  // debug


// unsigned char txbuf[100];
// unsigned char rxbuf[4000];


/* OTDR 测试数据命令 */
#define OTDR_RETURN      (0xf0)
#define OTDR_RESET       (0xe0)

#define OTDR_PARAM       (0x10)
#define OTDR_START       (0x11)
#define OTDR_STOP        (0x12)
#define OTDR_IS_FINISH   (0x13)
#define OTDR_REDLASER    (0x14)
#define OTDR_ALIVE       (0xa0)
#define OTDR_COM_LASER   (0x15)
#define OTDR_VER         (0xa1)
#define OTDR_ROTDR       (0x20)
#define OTDR_POWER       (0x22)

/* 数据错误试着次数 */
#define RETRY            (3)


/* 本接口可支持的软件版本 */
struct _fpga_table
{
    unsigned long soft_ver;
    unsigned long hw_ver;
    char	 *desc;
};


// static const struct _fpga_table table[] = {
// };
void memcpy_char(char *dst, char *src, int len)
{
    for (int i = 0; i < len; i++) {
	*dst = *src;
	dst++;
	src++;
    }
}


int fpga_close(struct fpga_dev *dev)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);
    mutex_destroy(&dev->mutex);
    close(dev->fd);
    free(dev);
    dev = NULL;

    // 全局的可能会引起其他线程崩溃，不同应用之间不用担心，依靠写时拷贝
    free(otdr_cache);
    otdr_cache = NULL;

    return 0;
}


int fpga_open(char *device, struct fpga_dev **dev, unsigned long ispeed)
{
    // todo spinlock
    unsigned char mode = 0;
    unsigned char bits = 16;
    // unsigned long speed = 12 * 1000 * 1000 ; // 12MHz
    // unsigned long speed = 500 * 1000 ; // 500KHz
    unsigned long speed = 10 * 1000 * 1000;
    // unsigned short delay;
    int fd;

    // if (ispeed) {
    // 	speed = ispeed;
    // }

    fd = open(device, O_RDWR);
    if (fd == 0) {
	return -1;
    }

    // mode = SPI_MODE_3;//ok
    mode = 0;  // ok
    // // mode = SPI_MODE_1;//error
    // mode = SPI_MODE_0;//ok
    ioctl(fd, SPI_IOC_WR_MODE, &mode);
    ioctl(fd, SPI_IOC_RD_MODE, &mode);
    ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
    printf("bits %d\r\n", bits);

    struct fpga_dev *pdev;
    pdev = (struct fpga_dev *)malloc(sizeof(struct fpga_dev));

    if (pdev == NULL) {
	goto _fail1;
    }
    bzero(pdev, sizeof(struct fpga_dev));

    // todo spinlock
    pdev->fd = fd;

    mutex_init(&pdev->mutex, NULL);
    if (-1 == fpga_alive(pdev)) {
	goto _fail2;
    }
    *dev       = pdev;
    otdr_cache = malloc(LEN_OTDR_CACHE);
    if (otdr_cache == NULL) {
	PRINTE("malloc otdr_cache \n");
	exit(1);
    }
    return 0;
_fail1:
    close(fd);
    return -1;
_fail2:
    fpga_close(pdev);
    return -2;
}

#define CONFIG_FPGA_SHIFT 1
/**
 * @brief	由于帧错位，在一串接受数据里找到帧头
 * @param	data 错位的帧
 * @param	limit 只在limit之前查找帧头，data 在 limit 后还有数据，但不足一帧
 * @retval	其他 找到帧
 * @retval	<0 找不到帧
 */

static int _find_frame_start(char *data, int limit)
{
#if defined(CONFIG_FPGA_SHIFT)
    char *pdata;
    for (int i = 0; i < limit; i++) {
	pdata = data + i;
	if ('G' == pdata[0] &&
	    'L' == pdata[1] &&
	    'I' == pdata[2] &&
	    'N' == pdata[3] &&
	    'K' == pdata[4]) {
	    this_dbg("%s() find frame head [offset %d byte]\r\n", __FUNCTION__, i);
	    return i;
	}
    }
    return -1;
#else
    return -1;
#endif
}
/**
 * @brief	寻找OTDR数据开始位置
 * @param	data
 * @param	limit 最大检测长度
 * @retval	null
 * @remarks
 * @see
 */
#if defined(CONFIG__find_otdr_data_start)
static int _find_otdr_data_start(char *data, int limit)
{
#if 1  // defined(CONFIG_FPGA_SHIFT)
    char *pdata;
    for (int i = 0; i < limit; i++) {
	pdata = data + i;
	if ('#' == pdata[0] &&
	    '#' == pdata[4] &&
	    '#' == pdata[8]) {
	    if (i != 0) {
		this_dbg("%s() find otdr data head [offset %d byte]\r\n", __FUNCTION__, i);
	    }
	    return i;
	}
    }
    return -1;
#else
    return 0;
#endif
}
#endif

static int _fpga_otdr_start(struct fpga_dev *dev)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    char rbuf[MIN_RX_PACK_SIZE];
    int  retry = 0;
_again:
    _fpga_transfer(dev, OTDR_START, NULL, 0, rbuf, ARRAY_SIZE(rbuf));

    unsigned char ack;
    int           offset = 0;
_CheckFrame:;
    ack = ACK_CODE(rbuf + offset);
    retry++;
    switch (ack) {
    case 0xaa:
	this_dbg("%s() OK\r\n", __FUNCTION__);
	return 0;
    case 0x55:
	PRINTW("Error\r\n");
	return -1;
    default:
	offset = _find_frame_start(rbuf, ARRAY_SIZE(rbuf) / 2);
	if (retry < RETRY || -1 == offset) {
	    PRINTE("unknow\r\n");
	    offset = 0;
	    break;
	}
	else {
	    goto _CheckFrame;
	}
	break;
    }

    if (retry < RETRY) {
	goto _again;
    }
    PRINTE("retry time out\r\n");
    return -1;
}
int fpga_otdr_start(struct fpga_dev *dev)
{
    mutex_lock(&dev->mutex);
    int ret = _fpga_otdr_start(dev);
    mutex_unlock(&dev->mutex);
    return ret;
}
int fpga_otdr_stop(struct fpga_dev *dev)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    char rbuf[MIN_RX_PACK_SIZE];

    mutex_lock(&dev->mutex);
    _fpga_transfer(dev, OTDR_STOP, NULL, 0, rbuf, ARRAY_SIZE(rbuf));
    mutex_unlock(&dev->mutex);

    unsigned char ack = ACK_CODE(rbuf);
    switch (ack) {
    case 0xaa:
	this_dbg("%s() OK\r\n", __FUNCTION__);
	return 0;
    case 0x55:
	PRINTW("Error\r\n");
	return -1;
    default:
	PRINTE("unknow\r\n");
	return -1;
    }
    return -1;
}

// 小端
#if 1
#define _STREAM_TO_32(data) (((data)[0] << 0) | ((data)[1] << 8) | ((data)[2] << 16) | ((data)[3] << 24))
#define _STREAM_TO_16(data) (((data)[0] << 0) | ((data)[1] << 8))
#else
#endif
int fpga_otdr_isfinish(struct fpga_dev *dev, bool *val)
{
    assert(dev != NULL);
    // assert(dev->desc == dev_desc);

    // char rbuf[MIN_RX_PACK_SIZE + 6];
    char rbuf[MIN_RX_PACK_SIZE];

    mutex_lock(&dev->mutex);
    _fpga_transfer(dev, OTDR_IS_FINISH, NULL, 0, rbuf, ARRAY_SIZE(rbuf));
    mutex_unlock(&dev->mutex);

    unsigned char ack;
    int           offset = 0;
    int           retry  = 0;

    *val = false;
_CheckFrame:;
    ack = ACK_CODE(rbuf + offset);
    retry++;
    switch (ack) {
    case 0xaa:
	dev->checksum = _STREAM_TO_32(&rbuf[CHKSUM_OFFSET]);
	*val          = true;
	return 0;
    case 0x55:
	dev->checksum = 0;
	*val          = false;
	return 0;
    default:
	offset = _find_frame_start(rbuf, ARRAY_SIZE(rbuf) / 2);
	if (retry < RETRY || -1 == offset) {
#if defined(CONFIG_OTDR_PRINT_RX) && defined(CONFIG_ISFINISH_RX)
	    PrintfMemory((uint8_t *)rbuf, ARRAY_SIZE(rbuf));
	    PRINTE("unknow\r\n");
#endif
	    offset = 0;
	    return -1;
	}
	else {
	    goto _CheckFrame;
	}
    }
    return -1;
}
int fpga_otdr_temp(struct fpga_dev *dev, int *val)
{
    return 0;
}

/*****************************************************************************
    设置 OTDR 测试参数 功能入口 fpga_otdr_param

 fpga_otdr_param
    _encode_dist_2_pulse/_encode_self_pulse
    _encode_dist_2_sample_freq/_encode_self_sample_freq
    _encode_laser
    _encode_adopt
    _encode_add_cnt
    _encode_power
    _encode_amp
    _encode_apd_vol
    _encode_slot
    _encode_ch
*****************************************************************************/
// static short _encode_laser(struct fpga_test_param *val);
// static short _encode_dist_2_pulse(struct fpga_test_param *val);
// static short _encode_self_pulse(struct fpga_test_param *val);
// static char  _encode_dist_2_sample_freq(struct fpga_test_param *val);
// static char  _encode_self_sample_freq(struct fpga_test_param *val);
// static char  _encode_adopt(struct fpga_test_param *val);
// static short  _encode_add_cnt(struct fpga_test_param *val);
// static char  _encode_amp(struct fpga_test_param *val);
// static char  _encode_apd_vol(struct fpga_test_param *val);
// static char  _encode_power(struct fpga_test_param *val);
// static char  _encode_slot(struct fpga_test_param *val);
// static char  _encode_ch(struct fpga_test_param *val);

/*
  struct _fpga_test_param_tx 内容依据《TR600plus—CPU与FPGA通信命令协议.doc》
  2016-11-24 协议格式而定

  实际发送 "数据段" 内容
 转译 struct fpga_test_param  用fpga_otdr_param() 屏蔽协议层、与协议无关
*/
// struct _fpga_test_param_tx {
// 	unsigned short laser;///< 激光器通道
// 	unsigned short pulse;
// 	unsigned char freq;
// 	unsigned char adopt_pt;
// 	unsigned short add_cnt;
// 	char power;
// 	char amp;
// 	char apd_vol;
// 	char slot;
// 	char ch;	///< 光开关通道
// 	char unuse1, unuse2, unuse3;
// };


// static short _encode_laser(struct fpga_test_param *val)
// {
// 	// assert(1310 == val->laser_ch ||
// 	//        1550 == val->laser_ch ||
// 	//        1490 == val->laser_ch ||
// 	//        1625 == val->laser_ch ||
// 	//        1300 == val->laser_ch ||
// 	//        // 换一种描述方式
// 	//        1 == val->laser_ch ||
// 	//        2 == val->laser_ch ||
// 	//        3 == val->laser_ch ||
// 	//        4 == val->laser_ch ||
// 	//        5 == val->laser_ch);

// 	return val->laser_ch;
// }


/*
  50MHz 采样时钟为基准，得到其他频率的分频系数

  Note
  高于 50MHz 不是采用倍频，而是移相，所以分频系数与 50MHz 相同
 */

struct _dist_div
{
    unsigned long dist;
    unsigned long div;
};
#define FREQ_800M_DIV (0)
#define FREQ_400M_DIV (0)
#define FREQ_200M_DIV (0)
#define FREQ_100M_DIV (0)
#define FREQ_50M_DIV  (0)
#define FREQ_25M_DIV  (1)
#define FREQ_12M_DIV  (2)
static const struct _dist_div _dd[] = {
    { 300, FREQ_800M_DIV },
    { 1000, FREQ_800M_DIV },
    { 5000, FREQ_400M_DIV },
    { 10000, FREQ_200M_DIV },
    { 30000, FREQ_100M_DIV },
    { 60000, FREQ_50M_DIV },
    { 100000, FREQ_25M_DIV },
    { 180000, FREQ_12M_DIV },
    { 200000, FREQ_12M_DIV },
    { 0, 0 },
};


// static short _encode_dist_2_pulse(struct fpga_test_param *val)
// {
// 	// 5ns 10ns 采用自然编码
// 	if (val->pulse < 20) {
// 		return val->pulse;
// 	}
// 	// TODO 开发期间过滤掉非20ns的倍数
// 	const struct _dist_div *tlist = (struct _dist_div *)_dd;

// 	while(tlist->dist) {
// 		if (tlist->dist == val->dist ) {
// 			break;
// 		}
// 		tlist++;
// 	}
// 	assert(tlist->dist != 0);

// 	if (tlist->dist == 0) {
// 		// 参数错误采用默认值
// 		PRINTE("Error Param distance\r\n");
// 		return 10;
// 	}
// 	return (val->pulse / 20) >> tlist->div | 0x8000;
// }


struct _list_self_pulse
{
    int freq;  // 采样频率描述值，单位为 MHz
    /*
      采样频率编码值，数据类型与
      struct _fpga_test_param_tx -> freq 一致
    */
    unsigned char div;
};

static const struct _list_self_pulse _l_self_pulse[] = {
    { 800, FREQ_800M_DIV },
    { 400, FREQ_400M_DIV },
    { 200, FREQ_200M_DIV },
    { 100, FREQ_100M_DIV },
    { 50, FREQ_50M_DIV },
    { 25, FREQ_25M_DIV },
    { 12, FREQ_12M_DIV },
    { 0, 0 },
};

// static short _encode_self_pulse(struct fpga_test_param *val)
// {
// 	// 5ns 10ns 采用自然编码
// 	if (val->pulse < 20) {
// 		return val->pulse;
// 	}
// 	// TODO 开发期间过滤掉非20ns的倍数
// 	struct _list_self_pulse *tlist = (struct _list_self_pulse *)_l_self_pulse;

// 	while(tlist->freq) {
// 		if (tlist->freq == val->sample_freq ) {
// 			break;
// 		}
// 		tlist++;
// 	}
// 	assert(tlist->freq != 0);

// 	if (tlist->freq == 0) {
// 		// 参数错误采用默认值
// 		PRINTE("Error Param distance\r\n");
// 		return 10;
// 	}
// 	// return 0x8001;//
// 	return (val->pulse / 20) >> tlist->div | 0x8000;
// }


// **************************************************************************


struct _dist_sample_freq
{
    /*
      正常情况下，
      每个量程下的采用频率是固定的，如不用该对应关系则用
      struct _list_self_sample_freq 描述
    */
    unsigned long dist;
    unsigned char sample_freq;
};
#define FREQ_800M (0x90)
#define FREQ_400M (0x88)
#define FREQ_200M (0x84)
#define FREQ_100M (0x82)
#define FREQ_50M  (0x80)
#define FREQ_25M  (0x7e)
#define FREQ_12M  (0x7c)

static const struct _dist_sample_freq _sample_freq[] = {
    { 300, FREQ_800M },
    { 1000, FREQ_800M },
    { 5000, FREQ_400M },
    { 10000, FREQ_200M },
    { 30000, FREQ_100M },
    { 60000, FREQ_50M },
    { 100000, FREQ_25M },
    { 180000, FREQ_12M },
    { 200000, FREQ_12M },
    { 0, 0 },
};

// static char _encode_dist_2_sample_freq(struct fpga_test_param *val)
// {
// 	// 查找对应量程需要使用的频率
// 	struct _dist_sample_freq *tlist = (struct _dist_sample_freq *)_sample_freq;

// 	while(tlist->dist) {
// 		if (tlist->dist == val->dist ) {
// 			break;
// 		}
// 		tlist++;
// 	}
// 	assert(tlist->dist != 0);

// 	if (tlist->dist == 0) {
// 		// 参数错误采用默认值
// 		PRINTE("Error Param distance\r\n");
// 		return FREQ_800M;
// 	}
// 	return tlist->sample_freq;
// }


struct _list_self_sample_freq
{
    int val;  // 采样频率描述值，单位为 MHz
    /*
      采样频率编码值，数据类型与
      struct _fpga_test_param_tx -> freq 一致
    */
    unsigned char encode;
};

static const struct _list_self_sample_freq _l_sample_freq[] = {
    { 800, FREQ_800M },
    { 400, FREQ_400M },
    { 200, FREQ_200M },
    { 100, FREQ_100M },
    { 50, FREQ_50M },
    { 25, FREQ_25M },
    { 12, FREQ_12M },
    { 0, 0 },
};
// 自定义采样频率
// static char _encode_self_sample_freq(struct fpga_test_param *val)
// {
// 	struct _list_self_sample_freq *tlist = (struct _list_self_sample_freq *)_l_sample_freq;

// 	while(tlist->val) {
// 		if (val->sample_freq ==  tlist->val) {
// 			break;
// 		}
// 		tlist++;
// 	}
// 	assert(tlist->val != 0);

// 	if (tlist->val == 0) {
// 		// 参数错误采用默认值
// 		PRINTE("Param Error\r\n");
// 		return FREQ_800M;
// 	}
// 	return tlist->encode;
// }


// **************************************************************************


// static char _encode_adopt(struct fpga_test_param *val)
// {
// 	switch(val->adopt_pt) {
// 	case 32000:
// 		return 8;
// 		break;
// 	case 4000:
// 		return 1;
// 		break;
// 	case 8000:
// 		return 2;
// 		break;
// 	case 16000:
// 		return 4;
// 		break;
// 	case 64000:
// 		return 16;
// 		break;
// 	case 128000:
// 		return 32;
// 		break;
// 	case 256000:
// 		return 64;
// 		break;
// 	}
// 	return 8;
// }

// static short _encode_add_cnt(struct fpga_test_param *val)
// {
// 	return val->add_cnt;
// }


//**************************************************************
// 有效参数等待 FPGA 解释
// 未必是下面的有效值
#define UNUSE_BIT          (0)

#define AMP_LOW_ACTIVE_LV1 (CFG_AMP_LOW_ACTIVE_LV1)  // 光耦开关低电平有效
#define AMP_LOW_ACTIVE_LV2 (CFG_AMP_LOW_ACTIVE_LV2)
#define AMP_LOW_ACTIVE_LV3 (CFG_AMP_LOW_ACTIVE_LV3)
#define AMP_LOW_ACTIVE_LV4 (CFG_AMP_LOW_ACTIVE_LV4)


#define AMP_B0_TO_SW       (CFG_AMP_B0_TO_SW)  // bit0 映射到amp编码值 0x80上
#define AMP_B1_TO_SW       (CFG_AMP_B1_TO_SW)
#define AMP_B2_TO_SW       (CFG_AMP_B2_TO_SW)
#define AMP_B3_TO_SW       (CFG_AMP_B3_TO_SW)
#define AMP_B4_TO_SW       (CFG_AMP_B4_TO_SW)
#define AMP_B5_TO_SW       (CFG_AMP_B5_TO_SW)
#define AMP_B6_TO_SW       (CFG_AMP_B6_TO_SW)
#define AMP_B7_TO_SW       (CFG_AMP_B7_TO_SW)

#define AMP_B8_TO_SW       (CFG_AMP_B8_TO_SW)
#define AMP_B9_TO_SW       (CFG_AMP_B9_TO_SW)
#define AMP_B10_TO_SW      (CFG_AMP_B10_TO_SW)
#define AMP_B11_TO_SW      (CFG_AMP_B11_TO_SW)
#define AMP_B12_TO_SW      (CFG_AMP_B12_TO_SW)
#define AMP_B13_TO_SW      (CFG_AMP_B13_TO_SW)
#define AMP_B14_TO_SW      (CFG_AMP_B14_TO_SW)
#define AMP_B15_TO_SW      (CFG_AMP_B15_TO_SW)

#define AMP_B16_TO_SW      (CFG_AMP_B16_TO_SW)
#define AMP_B17_TO_SW      (CFG_AMP_B17_TO_SW)
#define AMP_B18_TO_SW      (CFG_AMP_B18_TO_SW)
#define AMP_B19_TO_SW      (CFG_AMP_B19_TO_SW)
#define AMP_B20_TO_SW      (CFG_AMP_B20_TO_SW)
#define AMP_B21_TO_SW      (CFG_AMP_B21_TO_SW)
#define AMP_B22_TO_SW      (CFG_AMP_B22_TO_SW)
#define AMP_B23_TO_SW      (CFG_AMP_B23_TO_SW)

#define AMP_B24_TO_SW      (CFG_AMP_B24_TO_SW)
#define AMP_B25_TO_SW      (CFG_AMP_B25_TO_SW)
#define AMP_B26_TO_SW      (CFG_AMP_B26_TO_SW)
#define AMP_B27_TO_SW      (CFG_AMP_B27_TO_SW)
#define AMP_B28_TO_SW      (CFG_AMP_B28_TO_SW)
#define AMP_B29_TO_SW      (CFG_AMP_B29_TO_SW)
#define AMP_B30_TO_SW      (CFG_AMP_B30_TO_SW)
#define AMP_B31_TO_SW      (CFG_AMP_B31_TO_SW)

// amp 各放大级别在 encode 里的有效掩码
#define AMP_MASK_LV1       (CFG_AMP_MASK_LV1)
#define AMP_MASK_LV2       (CFG_AMP_MASK_LV2)
#define AMP_MASK_LV3       (CFG_AMP_MASK_LV3)
#define AMP_MASK_LV4       (CFG_AMP_MASK_LV4)


// static char _encode_amp(struct fpga_test_param *val)
// {
// 	// 限制在5个可控档位以内
// 	char b0, b1, b2, b3, b4, b5, b6, b7;// 各比特位
// 	char level;
// 	char encode_lv1 = 0, encode_lv2 = 0, encode_lv3 = 0, encode_lv4 = 0;
// 	char encode = 0;

// #if AMP_MASK_LV1
// 	level = val->amp & 0xff;
// 	b0 = (level & 0x01) ? AMP_B0_TO_SW : 0;
// 	b1 = (level & 0x02) ? AMP_B1_TO_SW : 0;
// 	b2 = (level & 0x04) ? AMP_B2_TO_SW : 0;
// 	b3 = (level & 0x08) ? AMP_B3_TO_SW : 0;
// 	b4 = (level & 0x10) ? AMP_B4_TO_SW : 0;
// 	b5 = (level & 0x20) ? AMP_B5_TO_SW : 0;
// 	b6 = (level & 0x40) ? AMP_B6_TO_SW : 0;
// 	b7 = (level & 0x80) ? AMP_B7_TO_SW : 0;

// #if (AMP_LOW_ACTIVE_LV1 == 1)
// 	encode_lv1 =  ~(b0 ^ b1 ^ b2 ^ b3 ^ b4 ^ b5 ^ b6 ^ b7);// 硬件高电平有效
// #else
// 	encode_lv1 =   (b0 ^ b1 ^ b2 ^ b3 ^ b4 ^ b5 ^ b6 ^ b7);// 硬件低电平有效
// #endif
// 	encode_lv1 &= AMP_MASK_LV1;// 去掉无用 bit
// #endif // end AMP_MASK_LV1


// #if AMP_MASK_LV2
// 	level = (val->amp >> 8 ) & 0xff;
// 	b0 = (level & 0x01) ? AMP_B8_TO_SW : 0;
// 	b1 = (level & 0x02) ? AMP_B9_TO_SW : 0;
// 	b2 = (level & 0x04) ? AMP_B10_TO_SW : 0;
// 	b3 = (level & 0x08) ? AMP_B11_TO_SW : 0;
// 	b4 = (level & 0x10) ? AMP_B12_TO_SW : 0;
// 	b5 = (level & 0x20) ? AMP_B13_TO_SW : 0;
// 	b6 = (level & 0x40) ? AMP_B14_TO_SW : 0;
// 	b7 = (level & 0x80) ? AMP_B15_TO_SW : 0;
// #if (AMP_LOW_ACTIVE_LV2 == 1)
// 	encode_lv2 =  ~(b0 ^ b1 ^ b2 ^ b3 ^ b4 ^ b5 ^ b6 ^ b7);
// #else
// 	encode_lv2 =   (b0 ^ b1 ^ b2 ^ b3 ^ b4 ^ b5 ^ b6 ^ b7);
// #endif
// 	encode_lv2 &= AMP_MASK_LV2;
// #endif // end AMP_MASK_LV2


// #if AMP_MASK_LV3
// 	level = (val->amp >> 16 ) & 0xff;
// 	b0 = (level & 0x01) ? AMP_B16_TO_SW : 0;
// 	b1 = (level & 0x02) ? AMP_B17_TO_SW : 0;
// 	b2 = (level & 0x04) ? AMP_B18_TO_SW : 0;
// 	b3 = (level & 0x08) ? AMP_B19_TO_SW : 0;
// 	b4 = (level & 0x10) ? AMP_B20_TO_SW : 0;
// 	b5 = (level & 0x20) ? AMP_B21_TO_SW : 0;
// 	b6 = (level & 0x40) ? AMP_B22_TO_SW : 0;
// 	b7 = (level & 0x80) ? AMP_B23_TO_SW : 0;
// #if (AMP_LOW_ACTIVE_LV3 == 1)
// 	encode_lv3 =  ~(b0 ^ b1 ^ b2 ^ b3 ^ b4 ^ b5 ^ b6 ^ b7);
// #else
// 	encode_lv3 =   (b0 ^ b1 ^ b2 ^ b3 ^ b4 ^ b5 ^ b6 ^ b7);
// #endif
// 	encode_lv3 &= AMP_MASK_LV3;
// #endif // end AMP_MASK_LV3


// #if AMP_MASK_LV4
// 	level = (val->amp >> 24 ) & 0xff;
// 	b0 = (level & 0x01) ? AMP_B24_TO_SW : 0;
// 	b1 = (level & 0x02) ? AMP_B25_TO_SW : 0;
// 	b2 = (level & 0x04) ? AMP_B26_TO_SW : 0;
// 	b3 = (level & 0x08) ? AMP_B27_TO_SW : 0;
// 	b4 = (level & 0x10) ? AMP_B28_TO_SW : 0;
// 	b5 = (level & 0x20) ? AMP_B29_TO_SW : 0;
// 	b6 = (level & 0x40) ? AMP_B30_TO_SW : 0;
// 	b7 = (level & 0x80) ? AMP_B31_TO_SW : 0;

// #if (AMP_LOW_ACTIVE_LV4 == 1)
// 	encode_lv4 =  ~(b0 ^ b1 ^ b2 ^ b3 ^ b4 ^ b5 ^ b6 ^ b7);
// #else
// 	encode_lv4 =   (b0 ^ b1 ^ b2 ^ b3 ^ b4 ^ b5 ^ b6 ^ b7);
// #endif
// 	encode_lv4 &= AMP_MASK_LV4;
// #endif // end AMP_MASK_LV4

// 	encode = (encode_lv1 + encode_lv2 + encode_lv3 + encode_lv4) &
// 	         (AMP_MASK_LV1 + AMP_MASK_LV2 + AMP_MASK_LV3 + AMP_MASK_LV4);

// 	return encode;
// }


//**************************************************************
// 有效参数等待 FPGA 解释
// 未必是下面的有效值

#define APD_LOW_ACTIVE     (CFG_APD_LOW_ACTIVE)  // 光耦开关低电平有效
#define APD_LOW_ACTIVE     (CFG_APD_LOW_ACTIVE)
#define APD_LOW_ACTIVE     (CFG_APD_LOW_ACTIVE)
#define APD_LOW_ACTIVE     (CFG_APD_LOW_ACTIVE)


#define APD_B0_TO_SW       (CFG_APD_B0_TO_SW)  // bit0 映射到APD编码值 0x80上
#define APD_B1_TO_SW       (CFG_APD_B1_TO_SW)
#define APD_B2_TO_SW       (CFG_APD_B2_TO_SW)
#define APD_B3_TO_SW       (CFG_APD_B3_TO_SW)
#define APD_B4_TO_SW       (CFG_APD_B4_TO_SW)
#define APD_B5_TO_SW       (CFG_APD_B5_TO_SW)
#define APD_B6_TO_SW       (CFG_APD_B6_TO_SW)
#define APD_B7_TO_SW       (CFG_APD_B7_TO_SW)

#define APD_MASK           (CFG_APD_MASK)

// static char _encode_apd_vol(struct fpga_test_param *val)
// {
// 	char b0, b1, b2, b3, b4, b5, b6, b7;// 各比特位
// 	char level;
// 	char encode = 0;

// #if APD_MASK
// 	level = val->apd_vol & 0xff;
// 	b0 = (level & 0x01) ? APD_B0_TO_SW : 0;
// 	b1 = (level & 0x02) ? APD_B1_TO_SW : 0;
// 	b2 = (level & 0x04) ? APD_B2_TO_SW : 0;
// 	b3 = (level & 0x08) ? APD_B3_TO_SW : 0;
// 	b4 = (level & 0x10) ? APD_B4_TO_SW : 0;
// 	b5 = (level & 0x20) ? APD_B5_TO_SW : 0;
// 	b6 = (level & 0x40) ? APD_B6_TO_SW : 0;
// 	b7 = (level & 0x80) ? APD_B7_TO_SW : 0;

// #if (APD_LOW_ACTIVE == 1)
// 	encode =  ~(b0 ^ b1 ^ b2 ^ b3 ^ b4 ^ b5 ^ b6 ^ b7);// 硬件高电平有效
// #else
// 	encode =   (b0 ^ b1 ^ b2 ^ b3 ^ b4 ^ b5 ^ b6 ^ b7);// 硬件低电平有效
// #endif
// 	encode &= APD_MASK;// 去掉无用 bit
// #endif // end APD_LOW_ACTIVE

// 	return encode;
// }

char _list_power[] = {
    0,
    OTDR_LASER_PW_LV1,
    OTDR_LASER_PW_LV2,
    OTDR_LASER_PW_LV3,
    OTDR_LASER_PW_LV4,
};
// static char _encode_power(struct fpga_test_param *val)
// {
// 	assert(val->power < ARRAY_SIZE(_list_power));
// 	assert(0 <= val->power);

// 	return _list_power[(uint32_t)val->power];

// }
// static char _encode_slot(struct fpga_test_param *val)
// {
// 	// PRINTD("do nothing");
// 	return 0;
// }
// static char _encode_ch(struct fpga_test_param *val)
// {
// 	// PRINTD("do nothing");
// 	return val->panel_ch;
// }

static int _fpga_otdr_param(struct fpga_dev *dev, struct fpga_test_param *val)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    struct _fpga_test_param_tx tbuf;


    // printf("dist		%ld\r\n", val->dist);
    // printf("laser_freq	%d\r\n", val->laser_freq);
    // printf("pulse		%d\r\n", val->pulse);
    // printf("sample_freq	%d\r\n", val->sample_freq);
    // printf("adopt_pt	%ld\r\n", val->adopt_pt);
    // printf("add_cnt		%d\r\n", val->add_cnt);
    // printf("add_ms		%d\r\n", val->add_ms);
    // printf("laser_ch		%d\r\n", val->laser_ch);
    /*
      当 sample_freq 未指定时使用默认采样频率，并计算脉冲宽度编码，
      否则使用自定义采样频率
    */
    if (val->sample_freq == 0) {
	tbuf.pulse = _encode_dist_2_pulse(val);
	tbuf.freq  = _encode_dist_2_sample_freq(val);
    }
    else {
	tbuf.pulse = _encode_self_pulse(val);
	tbuf.freq  = _encode_self_sample_freq(val);
    }

    tbuf.laser    = _encode_laser(val);
    tbuf.adopt_pt = _encode_adopt(val);
    tbuf.add_cnt  = _encode_add_cnt(val);
    tbuf.power    = _encode_power(val);

    if (val->en_hw_amp) {
	tbuf.amp = val->hw_amp;
    }
    else {
	tbuf.amp = _encode_amp(val);
    }

    if (val->en_hw_apd_vol) {
	tbuf.apd_vol = val->hw_apd_vol;
    }
    else {
	tbuf.apd_vol = _encode_apd_vol(val);
    }
    tbuf.slot   = _encode_slot(val);
    tbuf.ch     = _encode_ch(val);
    tbuf.unuse1 = 0;
    tbuf.unuse2 = 0;
    tbuf.unuse3 = 0;

    this_dbg("    laser             %4.4x\n", tbuf.laser);
    this_dbg("    pulse             %4.4x\n", tbuf.pulse);
    this_dbg("    freq              %2.2x\n", tbuf.freq);
    this_dbg("    adopt_pt          %2.2x\n", tbuf.adopt_pt);
    this_dbg("    add_cnt           %4.4x\n", tbuf.add_cnt);
    this_dbg("    power             %2.2x\n", tbuf.power);
    this_dbg("    amp               %2.2x\n", tbuf.amp);
    this_dbg("    apd_vol           %2.2x\n", tbuf.apd_vol);
    this_dbg("    slot              %2.2x\n", tbuf.slot);
    this_dbg("    ch                %2.2x\n", tbuf.ch);
    this_dbg("    unuse1            %2.2x\n", tbuf.unuse1);
    this_dbg("    unuse2            %2.2x\n", tbuf.unuse2);
    this_dbg("    unuse3            %2.2x\n", tbuf.unuse3);


    char rbuf[MIN_RX_PACK_SIZE];
    int  retry = 0;
_again:
    _fpga_transfer(dev, OTDR_PARAM, (char *)&tbuf, sizeof(struct _fpga_test_param_tx), rbuf, ARRAY_SIZE(rbuf));

    unsigned char ack = ACK_CODE(rbuf);
    retry++;
    switch (ack) {
    case 0xaa:
	this_dbg("%s() OK\r\n", __FUNCTION__);
	// TODO 如果两次调用 fpga_otdr_param 之间没有调用 fpga_exist_com_laser 则不延时
	usleep(10000);  // FPGA控制光耦
	return 0;
    case 0x55:
	PRINTW("Error\r\n");
	break;
    default:
	PRINTE("unknow\r\n");
	break;
    }

    if (retry < RETRY) {
	goto _again;
    }
    PRINTE("retry time out\r\n");

    return -1;
}
int fpga_otdr_param(struct fpga_dev *dev, struct fpga_test_param *val)
{
    mutex_lock(&dev->mutex);
    int ret = _fpga_otdr_param(dev, val);
    mutex_unlock(&dev->mutex);
    return ret;
}
/*****************************************************************************
    End 设置 OTDR 测试参数 功能入口 fpga_otdr_param
*****************************************************************************/

// 请求和开始测试一起
int fpga_request_test(struct fpga_dev *dev, struct fpga_test_param *val)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    mutex_lock(&dev->mutex);
    if (0 == _fpga_otdr_param(dev, val) &&
        0 == _fpga_otdr_start(dev)
        // TODO 等待数据准备完好，并带超市机制
    ) {
	mutex_unlock(&dev->mutex);
	return 0;
    }
    else {
	mutex_unlock(&dev->mutex);
	return -1;
    }
}

int fpga_red_laser(struct fpga_dev *dev, int val)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);
    assert(val == OTDR_RL_OFF || val == OTDR_RL_ON || val == OTDR_RL_1HZ || val == OTDR_RL_10HZ);

    char tbuf[2];
    char rbuf[MIN_RX_PACK_SIZE];

    tbuf[0] = val;
    tbuf[1] = 0;
    mutex_unlock(&dev->mutex);
    _fpga_transfer(dev, OTDR_REDLASER, tbuf, ARRAY_SIZE(tbuf), rbuf, ARRAY_SIZE(rbuf));
    mutex_unlock(&dev->mutex);

    unsigned char ack = ACK_CODE(rbuf);

    switch (ack) {
    case 0xaa:
	this_dbg("%s() OK\r\n", __FUNCTION__);
	return 0;
    case 0x55:
	PRINTW("Error\r\n");
	return -1;
    default:
	PRINTE("unknow\r\n");
	return -1;
    }
}


/**
 * @brief	FPGA电源
 * @param	val 1 打开，0关闭
 *
 * @bug
 * 			以后这个命令可能演化成FPGA电源控制，
 *			使用这个命令关闭电源后，目前状态下FPGA接收的命令全是正确，
 *			导致通信光检测一直有通信光
 */
int fpga_power(struct fpga_dev *dev, int val)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    char tbuf[2];
    char rbuf[MIN_RX_PACK_SIZE];

    tbuf[0] = val;
    tbuf[1] = 0;
    mutex_unlock(&dev->mutex);
    _fpga_transfer(dev, OTDR_POWER,
                   // tbuf, ARRAY_SIZE(tbuf),
                   NULL,
                   0,
                   rbuf,
                   ARRAY_SIZE(rbuf));
    mutex_unlock(&dev->mutex);

    unsigned char ack = ACK_CODE(rbuf);

    switch (ack) {
    case 0xaa:
	this_dbg("%s() OK\r\n", __FUNCTION__);
	return 0;
    case 0x55:
	PRINTW("Error\r\n");
	return -1;
    default:
	PRINTE("unknow\r\n");
	return -1;
    }
}

/**
 * @brief	计算发送数据的校验码
 * @param[in]	data 待校验内容
 * @param[in]	len 待校验长度 byte 为单位
 * @retval	校验值
 */

unsigned short _tx_checksum(uint8_t *data, int len)
{
    unsigned short sum = 0;
    for (int i = 0; i < len; i++) {
	sum += data[i];
    }
    return sum;
}

unsigned char _remove_left_zero(unsigned char mask)
{
    if (0 == mask) {
	return 0;
    }
    while (0 == (mask & 0x01)) {
	mask = mask >> 1;
    }
    return mask;
}
/**
 * @brief	探测FPGA是否挂接到总线上
 * @param[in]	dev 设备描述符
 * @retval	0 找到设备
 * @retval	-1 没找到设备
 */
// static unsigned int _fpga_alive(struct fpga_dev *dev)
unsigned int _fpga_alive(struct fpga_dev *dev)
{
    assert(dev != NULL);

    char rbuf[MIN_RX_PACK_SIZE];
    int  retry = 0;

_again:
    _fpga_transfer(dev, OTDR_ALIVE, NULL, 0, rbuf, ARRAY_SIZE(rbuf));

    unsigned char ack;
    int           offset = 0;

    // #ifdef CONFIG_IGNORE_FPGA_ALIVE
    // 	this_dbg("%s() OK CONFIG_IGNORE_FPGA_ALIVE\r\n", __FUNCTION__);
    // 	dev->desc = (char *)dev_desc;
    // 	dev->mask_amp = _remove_left_zero(CFG_AMP_MASK_LV1) |
    // 	                (_remove_left_zero(CFG_AMP_MASK_LV2) << 8 ) |
    // 	                (_remove_left_zero(CFG_AMP_MASK_LV3) << 16 ) |
    // 	                (_remove_left_zero(CFG_AMP_MASK_LV4) << 24 );
    // 	dev->mask_apd = CFG_APD_MASK;
    // 	return 0;
    // #endif
    dev->desc = (char *)dev_desc;
_CheckFrame:;

    ack = ACK_CODE(rbuf + offset);

    retry++;
    switch (ack) {
    case 0xaa:
	this_dbg("%s() OK\r\n", __FUNCTION__);
	dev->mask_amp = _remove_left_zero(CFG_AMP_MASK_LV1) |
	                (_remove_left_zero(CFG_AMP_MASK_LV2) << 8) |
	                (_remove_left_zero(CFG_AMP_MASK_LV3) << 16) |
	                (_remove_left_zero(CFG_AMP_MASK_LV4) << 24);
	dev->mask_apd = CFG_APD_MASK;
	return 0;
    case 0x55:
	PRINTW("Error\r\n");
	break;
    default:
	offset = _find_frame_start(rbuf, ARRAY_SIZE(rbuf) / 2);
	if (retry < RETRY || -1 == offset) {
	    PRINTE("unknow\r\n");
	    offset = 0;
	    break;
	}
	else {
	    goto _CheckFrame;
	}
    }
    if (retry < RETRY) {
	goto _again;
    }
    PRINTE("retry time out\r\n");


    return -1;
}
unsigned int fpga_alive(struct fpga_dev *dev)
{
    mutex_lock(&dev->mutex);
    int ret = _fpga_alive(dev);
    mutex_unlock(&dev->mutex);
    return ret;
}
/**
 * @remarks	----    fpga-2016 不实现    ----
 */
int fpga_lcd_bl(struct fpga_dev *dev, int val)
{
    return 0;
}
/**
 * @remarks	----    fpga-2016 不实现    ----
 */
int fpga_charge_status(struct fpga_dev *dev, char *val)
{
    return 0;
}
/**
 * @remarks	----    fpga-2016 不实现    ----
 */
int fpga_low_power(struct fpga_dev *dev, int val)
{
    return 0;
}


int fpga_exist_com_laser(struct fpga_dev *dev, bool *val)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    char rbuf[MIN_RX_PACK_SIZE];

    mutex_lock(&dev->mutex);
    _fpga_transfer(dev, OTDR_COM_LASER, NULL, 0, rbuf, ARRAY_SIZE(rbuf));
    mutex_unlock(&dev->mutex);

    unsigned char ack = ACK_CODE(rbuf);

    switch (ack) {
    case 0xaa:
	this_dbg("%s() exist\r\n", __FUNCTION__);
	*val = true;
	return 0;
    case 0x55:
	this_dbg("%s() free\r\n", __FUNCTION__);
	*val = false;
	return -1;
    default:
	PRINTE("unknow\r\n");
	*val = false;
	return -1;
    }
}

/* 接收到的版本信息转换成自然描述 */
#define HW_OFFSET   (11)
#define SOFT_OFFSET (12)

#define HW_VER(x)   (*(x + HW_OFFSET))
#define SOFT_VER(x) (*(x + SOFT_OFFSET))


int fpga_version(struct fpga_dev *dev, char *soft_ver, char *hw_ver)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    char rbuf[MIN_RX_PACK_SIZE];


    mutex_lock(&dev->mutex);
    _fpga_transfer(dev, OTDR_VER, NULL, 0, rbuf, ARRAY_SIZE(rbuf));
    mutex_unlock(&dev->mutex);

    unsigned char ack = ACK_CODE(rbuf);
    switch (ack) {
    case 0xaa:
	this_dbg("%s() OK\r\n", __FUNCTION__);
	// todu 分离版本号
	*soft_ver = SOFT_VER(rbuf);
	*hw_ver   = HW_VER(rbuf);
	return 0;
    case 0x55:
	PRINTW("Error\r\n");
	return -1;
    default:
	PRINTE("unknow\r\n");
	return -1;
    }
}

void PrintfMemory(uint8_t *buf, uint32_t len)
{
    for (uint32_t i = 0; i < len; i++) {
	if (i & 0xf) {
	    printf(" ");
	}
	else {
	    printf("\r\n");
	}
	printf("%2.2x", (unsigned char)*(buf + i));
    }
    printf("\r\n");
}


#if CONFIG_OTDR_PRINT_TX
void _show_cmd_name(char cmd)
{
    switch (cmd) {
    case OTDR_RETURN:
	printf("\tOTDR_RETURN\n");
	break;
    case OTDR_RESET:
	printf("\tOTDR_RESET\n");
	break;
    case OTDR_PARAM:
	printf("\tOTDR_PARAM\n");
	break;
    case OTDR_START:
	printf("\tOTDR_START\n");
	break;
    case OTDR_STOP:
	printf("\tOTDR_STOP\n");
	break;
    case OTDR_IS_FINISH:
	printf("\tOTDR_IS_FINISH\n");
	break;
    case OTDR_REDLASER:
	printf("\tOTDR_REDLASER\n");
	break;
    case OTDR_ALIVE:
	printf("\tOTDR_ALIVE\n");
	break;
    case OTDR_COM_LASER:
	printf("\tOTDR_COM_LASER\n");
	break;
    case OTDR_VER:
	printf("\tOTDR_VER\n");
	break;
    case OTDR_ROTDR:
	printf("\tOTDR_ROTDR\n");
	break;
    case OTDR_POWER:
	printf("\tOTDR_POWER\n");
    default:
	printf("     ----------Unknow\n");
	break;
    }
}
#endif


/**
 * @brief	fpga 通过SPI方式传输
 * @param[in]	dev 设备描述符
 * @param[in]	cmd 发送命令
 * @param[in]	tbuf 发送内容
 * @param[in]	tlen 发送长度（不超过4000byte）
 * @param[in]	rbuf 读取内容
 * @param[in]	rlen 读取长度（不超过4000byte）
 * @retval	null
 * @remark 调试版本中，在发送数据后固定延时5ms之后才读取数据，
    发布版可视情况删除
 */

unsigned int _fpga_transfer(struct fpga_dev *dev, unsigned char cmd, char *tbuf, int tlen, char *rbuf, int rlen)
{
    /*
    防止 struct fpga_2016 被编译器 n字节对齐，导致长度大于MIN_TX_PACK_SIZE
    末尾的 unsigned short 是校验码
    */

    assert(sizeof(struct fpga_2016) + sizeof(unsigned short) == MIN_TX_PACK_SIZE);
    char                    tx_head[sizeof(struct fpga_2016)];
    struct fpga_2016       *ptx     = (struct fpga_2016 *)tx_head;
    unsigned short          cksum   = 0;
    char                    unuse[] = { 0xff, 0xff, 0xff, 0xff };
    struct spi_ioc_transfer arr[]   = {
        {
		  .tx_buf    = (unsigned long)unuse,
		  .rx_buf    = (unsigned long)NULL,
		  .len       = ARRAY_SIZE(unuse),
		  .cs_change = 0,
        },
        {
		  .tx_buf    = (unsigned long)tx_head,
		  .rx_buf    = (unsigned long)NULL,
		  .len       = sizeof(struct fpga_2016),
		  .cs_change = 0,
        },
        {
		  .tx_buf    = (unsigned long)tbuf,
		  .rx_buf    = (unsigned long)NULL,
		  .len       = tlen,
		  .cs_change = 0,
        },
        {
		  .tx_buf      = (unsigned long)&cksum,
		  .rx_buf      = (unsigned long)NULL,
		  .len         = 2,
		  .cs_change   = 0,
		  .delay_usecs = 5000,  // 发送完后给 FPGA 响应时间，再读取
        },
        {
		  .tx_buf    = (unsigned long)NULL,
		  .rx_buf    = (unsigned long)rbuf,
		  .len       = rlen,
		  .cs_change = 1,
        },
    };
    ptx->head[0] = 'G', ptx->head[1] = 'L', ptx->head[2] = 'I', ptx->head[3] = 'N', ptx->head[4] = 'K';
    ptx->unuse0 = 0;
    ptx->unuse1 = 0;
    ptx->addr   = 0;
    ptx->len    = 10 + tlen;
    ptx->cmd    = cmd;
    cksum       = cmd;
    cksum += _tx_checksum((uint8_t *)tbuf, tlen);

#if CONFIG_OTDR_PRINT_TX
    if (DBG_TX) {
	printf("*****************************");
	_show_cmd_name(cmd);
	printf("tx: %d byte", sizeof(struct fpga_2016));
	PrintfMemory((uint8_t *)tx_head, sizeof(struct fpga_2016));
	printf("buf: %d byte", tlen);
	PrintfMemory((uint8_t *)tbuf, tlen);
	printf("cksum: %d byte", 2);
	PrintfMemory((uint8_t *)&cksum, 2);
    }
#endif

    ioctl(dev->fd, SPI_IOC_MESSAGE(ARRAY_SIZE(arr)), &arr);
#if CONFIG_OTDR_PRINT_RX
    if (DBG_RX) {
	if (rlen > 64) {
	    rlen = 64;
	}
#if !defined(CONFIG_ISFINISH_RX)
	if (cmd == OTDR_IS_FINISH) {
	    return -1;
	}
#endif
	PrintfMemory((uint8_t *)rbuf, rlen);
    }
#endif

    return -1;
}


/***************************************************************************
    OTDR 数据读取 功能入口 fpga_read_otdr_data

    fpga_read_otdr_data
        _read_otdr_first
        _read_otdr_serial
        _otdr_data_swap
        _print_otdr_data
        _otdr_checksum
            __checksum
***************************************************************************/

static unsigned int _read_otdr_serial(struct fpga_dev *dev, unsigned char *buf);
static void         _print_otdr_data(unsigned long *buf, int len);
static int          _otdr_data_swap(unsigned char *buf, int len);
#if defined(CONFIG__read_otdr_1)
static unsigned int _read_otdr_1(struct fpga_dev *dev, unsigned char *buf, int size);
#endif
#if defined(CONFIG__read_otdr_first)
static unsigned int _read_otdr_first(struct fpga_dev *dev, unsigned char *buf);
#endif

#if defined(CONFIG_USE_CKSUM)
static unsigned long __checksum(unsigned char *data, int len);
static unsigned long _otdr_checksum(unsigned char *data, unsigned long len);
#endif

#define ACK_OTDR_FRAME_LEN (18)

int fpga_read_otdr_data(
	struct fpga_dev *dev,
	unsigned char   *otdr_data,
	unsigned long    len)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);
    assert(len == 16000 * 4 || len == 32000 * 4 || len == 64000 * 4 || len == 128000 * 4);

    char *recv_otdr_buf = otdr_cache;

    /*
     * OTDR 数据格式，第一个0x23开头，后面每 4 byte为1数据点，每数据点 0x23开头
     * 0x23 | 0x23 xx xx xx | 0x23 xx xx xx | 0x23 xx xx xx
     *
     *         otdr_cache 最终存储的内容
     * |    0 byte             32000*4| 4000 |
     * |    OTDR 数据                 | 冗余 |
     * 后面的冗余内容是为了处理OTDR数据偏移而特意多读的
     */
    mutex_lock(&dev->mutex);

    int find_23 = 0;
    this_dbg("%s() search '#' \r\n", __FUNCTION__);
    for (int j = 0; j < 3; j++) {
	/*
	 * 接收OTDR数据，只读取 1byte
	 */
	_fpga_transfer(dev, OTDR_ROTDR, NULL, 0, recv_otdr_buf, 1);

	/*
	 * 逐个byte读取，在一定范围内寻找otdr数据帧头，
	 * 找到后就连快速读取后面的数据
	 */
	for (int i = 0; i < 100; i++) {
	    if (recv_otdr_buf[0] == '#') {
		find_23 = 1;
		goto _SerialRead;
	    }
	    _read_otdr_1(dev, (unsigned char *)(recv_otdr_buf), 1);
	}
    }


_SerialRead:;
    if (find_23 == 0) {
	PRINTE("otdr data errr: can't find flag '#' (0-100 byte) %2.2x %2.2x %2.2x %2.2x\r\n",
	       *(recv_otdr_buf + 0),
	       *(recv_otdr_buf + 1),
	       *(recv_otdr_buf + 2),
	       *(recv_otdr_buf + 3));
	mutex_unlock(&dev->mutex);
	return -1;
    }


#if defined(CONFIG_MAX_SPI_TR_4000BYTE)
    /*
     * 快速读取后面数据，_read_otdr_serial 每次读取4000个字节（OTDR数据1000个点）
     */
    for (int i = 0; i < LEN_OTDR_CACHE;) {
	_read_otdr_serial(dev, (unsigned char *)(recv_otdr_buf + i));

	i += 4000;  // 读取1000个点
    }
#elif defined(CONFIG_MAX_SPI_TR_32000BYTE)
    /*
     * 一次将所有数据全部读出，_read_otdr_serial 每次读取32*4000个字节（OTDR数据32000个点）
     * 目前只兼容16K、32K更高采样点需检测 len 后重新分配内存
     */
    _read_otdr_serial(dev, (unsigned char *)(recv_otdr_buf));

#endif


#if defined(CONFIG_OTDR_PRINT_RX)
    if (DBG_RX) {
	this_dbg("%s() peek receive data", __FUNCTION__);
	PrintfMemory((uint8_t *)recv_otdr_buf, 16);
    }
#endif

    memcpy_char((char *)otdr_data,
                (char *)(recv_otdr_buf + 0),
                len);
    mutex_unlock(&dev->mutex);


    /*
     * 数据都在 otdr_data 里，下面的数据doubleotdr_cache 的锁不再有效
     */
    __attribute__((unused)) int swap_err = _otdr_data_swap(otdr_data, len);
    _print_otdr_data((unsigned long *)otdr_data, len / 4);
#if defined(CONFIG_CHK_OTDR_FLAG) && !defined(CONFIG_USE_CKSUM)
    if (swap_err == -1) {
	return -1;
    }
    return 0;
#else
    unsigned long sum = 0;

    sum = _otdr_checksum(otdr_data, len);
    if (sum == dev->checksum) {
	this_dbg("%s() check sum OK\r\n", __FUNCTION__);
	return 0;
    }
    else {
	PRINTW("check sum error cksum [%x] otdr [%x]\n", (uint32_t)dev->checksum, (uint32_t)sum);
	return -1;
    }
#endif
}

/**
 * @brief	从SPI总线上读取若干字节
 * @param[in]	dev 设备描述符
 * @param[out]	buf 读取缓存
 * @param[in]	size 读取多少byte，如果该byte大于驱动缓存（默认4096），
 *		整个ioctl会报错，但函数不会有任何提示
 * @retval	0
 */
#if defined(CONFIG__read_otdr_1)
static unsigned int _read_otdr_1(struct fpga_dev *dev, unsigned char *buf, int size)
{
    struct spi_ioc_transfer arr[] = {
	{
		.tx_buf    = (unsigned long)NULL,
		.rx_buf    = (unsigned long)buf,
		.len       = size,
		.cs_change = 1,
	},
    };
    ioctl(dev->fd, SPI_IOC_MESSAGE(ARRAY_SIZE(arr)), &arr);
    return 0;
}
#endif
/**
 * @brief	@ref fpga_otdr_isfinish 保证fpga数据准备好后，向FPGA发送读取指令，
            读取otdr数据第一帧，以1000个采样点为一帧，每点4byte.
            _read_otdr_first 不应该被外部应用直接调用，通过对外接口
            @ref fpga_read_otdr_data 间接调用
 * @param[in]		dev 设备描述符
 * @param[in][out]	buf 接受一串otdr数据，此数据缓存有效长度必须大于4000byte
 * @retval	0
 * @see	fpga_otdr_isfinish @see fpga_read_otdr_data
 * @see _read_otdr_serial
 */
#if defined(CONFIG_read_otdr_first)
static unsigned int _read_otdr_first(struct fpga_dev *dev, unsigned char *buf)
{
    char                    tx_head[sizeof(struct fpga_2016)];
    struct fpga_2016       *ptx   = (struct fpga_2016 *)tx_head;
    unsigned short          cksum = 0;
    struct spi_ioc_transfer arr[] = {
	{
		.tx_buf    = (unsigned long)tx_head,
		.rx_buf    = (unsigned long)NULL,
		.len       = sizeof(struct fpga_2016),
		.cs_change = 0,
	},
	{
		.tx_buf    = (unsigned long)NULL,
		.rx_buf    = (unsigned long)NULL,
		.len       = 0,
		.cs_change = 0,
	},
	{
		.tx_buf    = (unsigned long)&cksum,
		.rx_buf    = (unsigned long)NULL,
		.len       = 2,
		.cs_change = 0,
	},
	{
		.tx_buf    = (unsigned long)NULL,
		.rx_buf    = (unsigned long)buf,
		.len       = 4000,
		.cs_change = 1,
	},
    };
    ptx->head[0] = 'G', ptx->head[1] = 'L', ptx->head[2] = 'I', ptx->head[3] = 'N', ptx->head[4] = 'K';
    ptx->len = 10;
    ptx->cmd = OTDR_ROTDR;
    cksum    = OTDR_ROTDR;
    cksum += _tx_checksum((uint8_t *)buf, 0);


    ioctl(dev->fd, SPI_IOC_MESSAGE(ARRAY_SIZE(arr)), &arr);

    return 0;
}
#endif

/**
 * @brief	@ref _read_otdr_first 读取第一帧之后，连续读取otdr数据剩下各帧，
        各帧之间没有任何命令码，以1000个采样点为一帧，每点4byte
        _read_otdr_serial 不应该被外部应用直接调用，通过对外接口
        @ref fpga_read_otdr_data 间接调用
 * @param[in]		dev 设备描述符
 * @param[in][out]	buf 接受一串otdr数据，此数据缓存有效长度必须大于4000byte
 * @retval	0
 * @see	_read_otdr_first fpga_read_otdr_data
 */

static unsigned int _read_otdr_serial(struct fpga_dev *dev, unsigned char *buf)
{
    struct spi_ioc_transfer arr[] = {
	{
		.delay_usecs = 1000,
		.tx_buf      = (unsigned long)NULL,
		.rx_buf      = (unsigned long)buf,
#if defined(CONFIG_MAX_SPI_TR_4000BYTE)
		.len = 4000,
#elif defined(CONFIG_MAX_SPI_TR_32000BYTE)
		.len = 4000 * 32,
#else
#error "unmatch board"
#endif
		.cs_change = 1,
	},
    };
    ioctl(dev->fd, SPI_IOC_MESSAGE(ARRAY_SIZE(arr)), &arr);
    return 0;
}

#define SWAP 1
/**
 * @brief	交换otdr数据
 * @param[in][out]	buf otdr数据流
 * @param[in]		len otdr数据流长度，1 byte为单位
 * @bug	数据内容采用大端格式，由来原因:\n
    fpga在传输数据时已4byte为单位，通常第一个byte最容易错误，如传输如下数据	\n
    0x00030201 0x00060504 						\n
    发送方										\n
    0x00 0x03 0x02 0x01 0x00 0x06 0x05 0x04		\n
    接收方 0xnn可能是乱码						\n
    0xnn 0x03 0x02 0x01 0xnn 0x06 0x05 0x04		\n
    接收方将最高位8bit清0，						\n
    0x00 0x03 0x02 0x01 0x00 0x06 0x05 0x04		\n
    接收方小端格式								\n
    0x01 0x02 0x03 0x00 0x04 0x05 0x06 0x00		\n
    接收以4byte为单位显示						\n
    0x00030201 0x00060504 						\n

 * @see fpga_read_otdr_data
 */
static int _otdr_data_swap(unsigned char *buf, int len)
{
#if defined(CONFIG_OTDR_SWAP)
    unsigned char *pchar = (unsigned char *)buf;
    char           tmp[4];

    for (int i = 0; i < len;) {
#if defined(CONFIG_CHK_OTDR_FLAG)
	if (pchar[0] != '#') {
	    PRINTE("error otdr data index %i\n", i);
	    return -1;
	}
#endif

#if defined(CONFIG_23_23aaBBcc_23aaBBcc)
	tmp[0] = pchar[0];
	tmp[1] = pchar[1];
	tmp[2] = pchar[2];
	tmp[3] = pchar[3];

	pchar[3] = 0;
	pchar[2] = tmp[1];
	pchar[1] = tmp[2];
	pchar[0] = tmp[3];
#endif  // end CONFIG_23_23aaBBcc_23aaBBcc
        // TODO 其他数据格式
	pchar += 4;
	i += 4;
    }
    return 0;
#else   // CONFIG_OTDR_SWAP
    return 0;
#endif  // end CONFIG_OTDR_SWAP
}

/**
 * @brief	格式化方式"简略"打印otdr数据(以4byte一个点为单位)
 * @param	buf otdr点数据
 * @param	len 点数
 * @remarks 需要编译时使能CONFIG_PRINT_OTDR_DATA，在运行时使能DBG_BIT_OTDR_DATA\n
    打印5000个点格式化:\n
 @code
    ..............0 - 10 (step 1)
    pt0001 pt0002 pt0003 pt0004 pt0005 pt0006 pt0007 pt0008 pt0009 pt0010
    ..............0 ~ 5000  (step 100)
    pt0100 pt0200 pt0300 pt0400 pt0500 pt0600 pt0700 pt0800 pt0900 pt1000
    pt1100 pt1200 pt1300 pt1400 pt1500 pt1600 pt1700 pt1800 pt1900 pt2000
    pt2100 pt2200 pt2300 pt2400 pt2500 pt2600 pt2700 pt2800 pt2900 pt3000
    pt3100 pt3200 pt3300 pt3400 pt3500 pt3600 pt3700 pt3800 pt3900 pt4000
    pt4100 pt4200 pt4300 pt4400 pt4500 pt4600 pt4700 pt4800 pt4900 pt5000
    ..............4991  ~  5000 (step 1)
    pt4991 pt4992 pt4993 pt4994 pt4995 pt4996 pt4997 pt4998 pt4999 pt5000
 @endcode
 */
static void _print_otdr_data(unsigned long *buf, int len)
{
#ifdef CONFIG_PRINT_OTDR_DATA
    if (0 == DBG_BIT_OTDR_DATA) {
	return;
    }
    unsigned long *plong;
    unsigned char *pchar;


    printf(C_DEBUG "\n..............0 - 10 (step 1)\n");
    plong = (unsigned long *)buf;

    for (int i = 0; i < 10; i++) {
	printf("%8.8x ", (unsigned int)*plong++);
    }

    printf("\n\n..............0 ~ %d  (step 100)", len);
    plong = (unsigned long *)buf;
    pchar = (unsigned char *)plong;
    // for (int i = 0; i < len / sizeof(unsigned long); i += 100) {
    for (int i = 0; i < len; i += 100) {
	if (i % 1000) {
	    printf(" ");
	}
	else {
	    printf("\n");
	}
	printf("%8.8x", (unsigned int)*(plong + i));
    }

    printf("\n\n..............%d  ~  %d (step 1)\n", len - 10, len);
    plong = (unsigned long *)(buf + len - 10);
    // plong = (unsigned long *)(buf + len- 10);
    for (int i = 0; i < 10; i++) {
	printf("%8.8x ", (unsigned int)*plong);
	plong++;
    }

    printf(C_NORMAL "\n");
#else
#endif
}

#if defined(CONFIG_DUMP_OTDR_DATA)
static void _save_file(char *fname, unsigned long *pt, unsigned long len)
{
    FILE *fp;
    fp = fopen(fname, "wb");

    for (int i = 0; i < len; i++) {
	fprintf(fp, "%8.8x\r\n", *(pt + i) & 0x00ffffff);
	// fprintf(fp, "%8.8x\r\n", *(pt + i));
    }
    fclose(fp);
}
#endif

/**
 * @brief	计算连续数据校验和，数据必须是4byte 倍数，数据已4byte为单位相加。
    如:\n
    0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08
    chksum = 0x04030201 + 0x08070605
 * @param	data 待计算数据
 * @param	len 数据长度，必须是4byte倍数
 * @retval 校验值
 * @see	_otdr_checksum
 */
#if defined(CONFIG_USE_CKSUM)
static unsigned long __checksum(unsigned char *data, int len)
{
    unsigned long     *plong;
    unsigned long      loop = len >> 2;
    unsigned long long sum  = 0, tmp;


#if defined(CONFIG_DUMP_CKSUM)
    FILE *fp;
    fp = fopen("cksum.txt", "wb");
#endif
    plong = (unsigned long *)data;

    for (int i = 0; i < loop; i++) {
	tmp = (unsigned long)data[0] +
	      (unsigned long)(data[1] << 8) +
	      (unsigned long)(data[2] << 16) +
	      (unsigned long)(data[3] << 24);
#if defined(CONFIG_DUMP_CKSUM)
	fprintf(fp, "%8.8x... \r\n", *plong);
#endif
	sum += *plong++;
    }
#if defined(CONFIG_DUMP_CKSUM)
    fclose(fp);
#endif

    return sum;
}
#endif
/**
 * @brief	计算OTDR数据校验和。若干次调用 @ref __checksum 计算出校验值，
        计算方法与实际设备特性有关，没有统一算法
 * @param	data 待计算数据
 * @param	len 数据长度，必须是4byte倍数
 * @retval 校验值
 * @see	__checksum
 */
#if defined(CONFIG_USE_CKSUM)
static unsigned long _otdr_checksum(unsigned char *data, unsigned long len)
{
    unsigned long sum = 0;

    /*
      otdr数据已4byte为一个点
      1. 去掉第一个点计算其余点校验和
      2. 原校验和 + 第一个点 "3次" 得出最终校验和
    */
    // sum =  __checksum(data + 4, len - 4);
    // sum += __checksum(data, 4);
    // sum += __checksum(data, 4);
    // sum += __checksum(data, 4);
#if defined(CONFIG_DUMP_OTDR_DATA)
    _save_file("otdr.txt", data, len / 4);
#endif
    sum = __checksum(data, len);
    return sum;
}
#endif
/***************************************************************************
    End OTDR 数据读取 功能入口 fpga_read_otdr_data
***************************************************************************/
struct sw
{
    unsigned int  ctrl;
    unsigned long bit;
};
unsigned long fpga_dbg_sw(struct fpga_dev *dev, struct fpga_test_param *param, int sw_index)
{
    struct sw list[] = {
	CFG_SW0,
	CFG_SW1,
	CFG_SW2,
	CFG_SW3,
	CFG_SW4,
	CFG_SW5,
	CFG_SW6,
	CFG_SW7,
	CFG_SW8,
	CFG_SW9,
	CFG_SW10,
	CFG_SW11,
	CFG_SW12,
	CFG_SW13,
	CFG_SW14,
	CFG_SW15,
	{ SW_UNUSE, UNUSE_BIT },
    };
    assert(sw_index < ARRAY_SIZE(list));
    if (sw_index >= ARRAY_SIZE(list)) {
	PRINTW("un support switch %d", sw_index);
	return -1;
    }
    param->en_hw_amp     = 1;
    param->hw_amp        = 0;
    param->en_hw_apd_vol = 1;
    param->hw_apd_vol    = 0;
    switch (list[sw_index].ctrl) {
    case SW_UNUSE:
	printf("switch unuse\n");

	break;
    case SW_RESERVE:
	printf("switch reserve\n");
	break;
    case SW_AMP:
	param->hw_amp = list[sw_index].bit;
	break;
    case SW_APD:
	param->hw_apd_vol = list[sw_index].bit;
	break;
    case SW_POWER:
	printf("switch power\n");
	break;
    }
    fpga_otdr_param(dev, param);
    fpga_otdr_start(dev);
    return 0;
}

int _fpga_send_data(
	struct fpga_dev *dev,
	char             cmd,
	void            *t_buf,
	int              tlen,
	void            *r_buf,
	int              rlen)
{
    char rbuf[MIN_RX_PACK_SIZE];
    _fpga_transfer(dev, cmd, t_buf, tlen, r_buf, rlen);
    char	 *pbuf = r_buf;
    unsigned char ack  = ACK_CODE(pbuf);
    switch (ack) {
    case 0xaa:
	this_dbg("%s() OK\r\n", __FUNCTION__);
	return 0;
    case 0x55:
	ENV_PRINT_W("Error\r\n");
	return -1;
    default:
	ENV_PRINT_E("unknow [ack] %d\r\n", ack);
	return -1;
    }
    return -1;
}
int fpga_send_data(
	struct fpga_dev *dev,
	char             cmd,
	void            *t_buf,
	int              tlen,
	void            *r_buf,
	int              rlen)
{
    mutex_lock(&dev->mutex);
    int ret = _fpga_send_data(dev, cmd, t_buf, tlen, r_buf, rlen);
    mutex_unlock(&dev->mutex);
    return ret;
}
#ifdef PRJ_NAME
MODULE_PROJECT(PRJ_NAME);
#endif
#if defined(PRJ_VERSION) && defined(PRJ_PATCHLEVEL) && defined(PRJ_SUBLEVEL)
MODULE_VERSION(PRJ_VERSION "." PRJ_PATCHLEVEL "." PRJ_SUBLEVEL);
#endif

MODULE_BUILD(__DATE__ " " __TIME__);
char str_config[] = "fffa";
MODULE_AUTHOR("MenglongWu <MenglongWoo@aliyun.com>");
MODULE_DEPEND("spi_mxs.ko spidev.ko libpthread.so");
MODULE_DESCRIPTION("The GLink OTDR FPGA communicate interface");
MODULE_DESCRIPTION("Board: TR600-Plus");
#ifdef CONFIG_CHK_OTDR_FLAG
PROG_INFO(config, "CONFIG_CHK_OTDR_FLAG=Y");
#else
PROG_INFO(config, "CONFIG_CHK_OTDR_FLAG=N");
#endif
#ifdef CONFIG_USE_CKSUM
PROG_INFO(config, "CONFIG_USE_CKSUM=Y");
#else
PROG_INFO(config, "CONFIG_USE_CKSUM=N");
#endif
#if defined(CONFIG_MAX_SPI_TR_4000BYTE)
PROG_INFO(config, "CONFIG_MAX_SPI_TR_4000BYTE=Y");
#elif defined(CONFIG_MAX_SPI_TR_32000BYTE)
PROG_INFO(config, "CONFIG_MAX_SPI_TR_32000BYTE=Y");
#else
#error("unmatch board");
#endif

PROG_GITSHA1(__GIT_SHA1__);
